﻿#region Copyright
// Copyright (c) 2011-2012 RaisingForce Team (Alikin Sergey)
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Linq;
using RaisingForce.Raise.Language.RStates;
using RaisingForce.Raise.Language.Rules;

namespace RaisingForce.Raise.Language.Model
{
    [ContractClass(typeof(WordTypeContract))]
    public abstract class WordType
    {
        #region Constructors

        protected WordType(
            string name,
            Type primaryGlyphType,
            Type secondaryGlyphType = null,
            IEnumerable<Type> alterableStateTypes = null)
        {
            if (!WordTypeRules.NameIsValid(name))
            {
                throw new ArgumentException("name");
            }
            if (primaryGlyphType == null)
            {
                throw new ArgumentNullException("primaryGlyphType");
            }
            if (!primaryGlyphType.IsSubclassOf(typeof(Glyph)))
            {
                throw new ArgumentException("primaryGlyphType");
            }
            if (secondaryGlyphType != null
                && !secondaryGlyphType.IsSubclassOf(typeof(Glyph)))
            {
                throw new ArgumentException("secondaryGlyphType");
            }
            Contract.Requires(
                alterableStateTypes == null
                || Contract.ForAll(
                    alterableStateTypes,
                    stateType => stateType != null && stateType.IsSubclassOf(typeof(RState))));
            Contract.EndContractBlock();
            this.name = name;
            this.primaryGlyphType = primaryGlyphType;
            this.secondaryGlyphType = secondaryGlyphType;
            IEnumerable<Type> usingAlterableStateTypes =
                alterableStateTypes != null ?
                alterableStateTypes.Distinct() :
                Enumerable.Empty<Type>();
            this.alterableStateTypes = usingAlterableStateTypes
                .ToList()
                .AsReadOnly();
        }

        #endregion

        #region Private Declarations

        private readonly string name;

        private readonly Type primaryGlyphType;

        private readonly Type secondaryGlyphType;

        private readonly ReadOnlyCollection<Type> alterableStateTypes;

        [ContractInvariantMethod]
        private void ObjectInvariant()
        {
            Contract.Invariant(WordTypeRules.NameIsValid(this.name));
            Contract.Invariant(this.primaryGlyphType != null);
            Contract.Invariant(this.primaryGlyphType.IsSubclassOf(typeof(Glyph)));
            Contract.Invariant(this.secondaryGlyphType == null || this.secondaryGlyphType.IsSubclassOf(typeof(Glyph)));
            Contract.Invariant(this.alterableStateTypes != null);
            Contract.Invariant(Contract.ForAll(
                this.alterableStateTypes,
                stateType => stateType != null && stateType.IsSubclassOf(typeof(RState))));
        }

        #endregion

        #region Abstract Properties

        protected abstract WordContext context { get; }

        #endregion

        #region Properties

        public WordContext Context
        {
            get
            {
                Contract.Ensures(Enum.IsDefined(typeof(WordContext), Contract.Result<WordContext>()));
                return this.context;
            }
        }

        public string Name
        {
            get
            {
                Contract.Ensures(WordTypeRules.NameIsValid(Contract.Result<string>()));
                return this.name;
            }
        }

        public Type PrimaryGlyphType
        {
            get
            {
                Contract.Ensures(Contract.Result<Type>() != null);
                Contract.Ensures(Contract.Result<Type>().IsSubclassOf(typeof(Glyph)));
                return this.primaryGlyphType;
            }
        }

        public Type SecondaryGlyphType
        {
            get
            {
                Contract.Ensures(Contract.Result<Type>() == null || Contract.Result<Type>().IsSubclassOf(typeof(Glyph)));
                return this.secondaryGlyphType;
            }
        }

        public ReadOnlyCollection<Type> AlterableStateTypes
        {
            get
            {
                Contract.Ensures(Contract.Result<ReadOnlyCollection<RState>>() != null);
                Contract.Ensures(Contract.ForAll(
                    Contract.Result<ReadOnlyCollection<Type>>(),
                    stateType => stateType != null && stateType.IsSubclassOf(typeof(RState))));
                return this.alterableStateTypes;
            }
        }

        #endregion
    }
}
